﻿using System;
using System.Collections;
using System.Linq.Expressions;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nvigorate.Common;
using Nvigorate.Extensions;

namespace Nvigorate.Test.Concepts
{
    /// <summary>
    /// Summary description for ProofOfConcept
    /// </summary>
    [TestClass]
    public class ProofOfConcept
    {
        public ProofOfConcept()
        {
            //
            // TODO: Add constructor logic here
            //
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region Flatten P.O.C.
        private object[] _jaggedArray = new object[]
                                            {
                                                "ax",
                                                "b",
                                                new string[] {"c", "d", "e"},
                                                "f",
                                                "g",
                                                new object[] {1, 2, 3}
                                            };
        private object[] _nestedArray = new object[]
                                            {
                                                new object[] {1, 2, 3}
                                            };

        [TestMethod]
        public void FlattenJaggedEnumerable()
        {
            IEnumerable<object> flattened = new List<object>()
                .Concat(
                    _jaggedArray
                        .Where(e => e.GetType() != typeof(string))
                        .OfType<IEnumerable>()
                        .SelectMany(e => e.OfType<object>()))
                .Concat(
                    _jaggedArray
                        .Where(o => o.GetType() == typeof(string) || o as IEnumerable == null)
                );

            List<object> list = flattened.ToList();

            Assert.IsTrue(list.Count == 10);
            Assert.IsTrue(list.Count == _jaggedArray.Flatten().Count());
        }

        [TestMethod]
        public void FlattenNestedEnumerable()
        {
            IEnumerable<object> flattened = new List<object>()
                .Concat(
                    _nestedArray
                        .Where(e => e.GetType() != typeof(string))
                        .OfType<IEnumerable>()
                        .SelectMany(e => e.OfType<object>()))
                .Concat(
                    _nestedArray
                        .Where(o => o.GetType() == typeof(string) || o as IEnumerable == null)
                );

            List<object> list = flattened.ToList();

            Assert.IsTrue(list.Count == 3);
            Assert.IsTrue(list.Count == _nestedArray.Flatten().Count());
        }

        #endregion

        #region Conditional Binding Lambda

        [TestMethod]
        public void TestBindingAMemberToItself()
        {
            Type targetType = typeof (BindingTarget);
            NewExpression newExpression = Expression.New(targetType);

            var bindings = new List<MemberBinding>();

            bindings.Add(
                Reflector.GetPropertyBindExpression(
                        targetType,
                        "Test1",
                        Expression.Constant("NotTest1")
                        ));

            MemberInitExpression initExpression = Expression.MemberInit(newExpression, bindings);
            Type functionType = Expression.GetFuncType(targetType);
            LambdaExpression lambdaExpression = Expression.Lambda(functionType, initExpression);
            Func<BindingTarget> lambda = (Func<BindingTarget>) lambdaExpression.Compile();

            BindingTarget test = lambda.Invoke();

            Assert.AreEqual(test.Test1, "test1");
            Assert.AreEqual(test.Test2, "test2");
        }


        public class BindingTarget
        {
            private string test1 = "test1";
            private string test2 = "test2";

            public string Test1
            {
                get { return test1; }
                set { test1 = value; }
            }

            public string Test2
            {
                get { return test2; }
                set { test2 = value; }
            }
        }

        #endregion
    }
}
